#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
Author: FQ
Module Description: item_utils ...
简单的道具处理模块，用于跨服道具扣除
File: item_utils.py
Date: 2021/4/26
"""
from pyshare.utils.game_log.game_log_define import ItemLog
from pyshare.utils.game_log.log_param import LogParam
from pyshare.dal.repository_impl.role_item_repository import ItemRepository, ActivityItemRepository
from pyshare.framework.error_handle import GameException
from pyshare.const.lang.tips_info import TipsInfo
from pyshare.const.enums.item import ItemType
from pyshare.bll.utils.area.res_utils import ResManager


class ItemBase(object):
    """
    道具对象
    """
    def __init__(self, rid, item_type, item_id, log_param=None, negative=False, count=None, **kwargs):
        """
        :param: rid: 角色rid
        :param: item_type：道具类型
        :param: item_id：道具id
        :param: log_param： 日志参数对象
        :param: negative： 是否可为负数
        :param: count: 默认扣除字段，不要轻易使用
        """
        self.rid = rid
        self.item_type = item_type
        self.item_id = item_id
        self.negative = negative
        self.object = self.get_record_object()
        self.log_param = log_param
        self.need_reduce_count = count

    def get_record_object(self):
        """
        获取道具记录对象
        """
        raise GameException('{} get_record_object error'.format(self.__class__))

    def get_num(self):
        """
        获取道具数量
        """
        raise GameException('{} get_num error'.format(self.__class__))

    def reduce(self, count):
        """
        扣除道具
        :param count: 扣除数量
        """
        raise GameException('{} reduce error'.format(self.__class__))

    def check(self, count):
        """
        校验数量是否足够
        """
        if self.get_num() < count:
            raise GameException(TipsInfo.ItemKeys.itemNotEnough)

    def left_count(self):
        """
        返回剩余数量
        return: [道具类型, 道具id, 剩余数量]
        """
        return [self.item_type, self.item_id, self.get_num()]


class TbItemOb(ItemBase):
    """
    道具对象
    适用于TbItem
    """
    # 仓储层对象
    Repository = ItemRepository

    def get_record_object(self):
        """
        获取道具记录对象
        """
        return ItemRepository.get_by_rid_itemtype_itemid(self.rid, self.item_type, self.item_id)

    def get_num(self):
        """
        获取道具数量
        """
        if not self.object:
            return 0

        return self.object.count

    def reduce(self, count):
        """
        扣除道具并记录日志
        :param count: 扣除数量
        """
        # 不允许扣负数时，需要校验当前剩余数量是否足够
        if not self.negative and (not self.object or self.object.count < count):
            # 道具数量不足
            raise GameException(TipsInfo.ItemKeys.itemNotEnough)

        self.object.count -= count
        if self.object.count == 0:
            # 需要删除国库记录
            self.Repository.delete(self.object)
            return self.object.count
        # 非0时，保存记录
        self.Repository.save(self.object)

        return self.object.count

    def log_item(self, count):
        """
        记录日志
        """
        # 记录日志
        if not self.log_param:
            # 外部没有传入日志的话，就用默认参数
            self.log_param = LogParam(rid=self.rid, remark='使用道具')
        ItemLog.log_game(log_param=self.log_param, item_type=self.item_type, item_id=self.item_id,
                         item_count=self.object.count, add_value=-count)

    def reduce_and_log(self, count):
        """
        扣除并记录日志
        """
        left_count = self.reduce(count)
        self.log_item(count)
        return left_count


class TbActivityItemOb(TbItemOb):
    """
    活动道具类对象
    适用于：TbActivityItem
    """
    # 仓储层对象
    Repository = ActivityItemRepository

    def __init__(self, rid, item_type, item_id, log_param=None, negative=False, count=None, activity_id=None):
        self.activity_id = activity_id
        super(TbActivityItemOb, self).__init__(rid, item_type, item_id, log_param=log_param, negative=negative, count=count)

    def get_record_object(self):
        """
        获取道具记录对象
        """
        return ActivityItemRepository.get_by_activityid_rid_itemtype_itemid(activity_id=self.activity_id, rid=self.rid,
                                                                            item_type=self.item_type, item_id=self.item_id)

    def log_item(self, count):
        """
        记录日志
        """
        # 记录日志
        if not self.log_param:
            # 外部没有传入日志的话，就用默认参数
            self.log_param = LogParam(rid=self.rid, remark='使用活动限时道具',
                                      reserve={"ActivityId": self.activity_id})
        ItemLog.log_game(log_param=self.log_param, item_type=self.item_type, item_id=self.item_id,
                         item_count=self.object.count, add_value=-count)


class TbBothItemOb(ItemBase):
    """
    特殊处理类对象
    适用于：同一个道具类型，在TbItem和TBActivityItem都有存储的道具
    比如 Character类型的道具
    """
    # TODO 逻辑待实现


class TbResourcesOb(ItemBase):
    """
    资源类对象
    适用于：TbResources
    """

    def get_record_object(self):
        """
        获取道具记录对象
        """
        res_manage = ResManager(self.rid)
        return res_manage

    def get_num(self):
        """
        获取道具数量
        """
        return self.object.get_res_by_type(self.item_type)

    def reduce(self, count):
        """
        扣除道具并记录日志
        :param count: 扣除数量
        """
        # 默认触发活动进度
        self.object.update_resource_by_type_and_log(self.item_type, -count, scene=self.log_param.scene,
                                                    remark=self.log_param.remark)

    def reduce_and_log(self, count):
        """
        扣除并记录日志,
        为保持和基类一致的调用方法
        """

        self.reduce(count)
        left_count = self.get_num()
        return left_count


class SimpleItemManager(object):
    """
    简易道具处理
    用途：主要提供给跨服使用，避免分段式调用，数据异常
    功能：道具扣除并记录日志
    道具范围：TbItem、资源
    """
    key_to = {
        # === 存储在TbItem
        ItemType.Numeric: TbItemOb,
        ItemType.ConcubineItem: TbItemOb,
        ItemType.Special: TbItemOb,
        ItemType.Chip: TbItemOb,
        ItemType.Treasure: TbItemOb,
        ItemType.Package: TbItemOb,
        ItemType.PeoNumeric: TbItemOb,
        ItemType.Peerage: TbItemOb,
        ItemType.Entity: TbItemOb,
        ItemType.PrinceSeekMate: TbItemOb,
        ItemType.Clothes: TbItemOb,
        ItemType.Red: TbItemOb,
        ItemType.ReusableActivity: TbItemOb,
        ItemType.Siege: TbItemOb,
        ItemType.Taxation: TbItemOb,
        ItemType.MultipleNumeric: TbItemOb,
        ItemType.Dinner: TbItemOb,
        ItemType.Visit: TbItemOb,
        ItemType.Guard: TbItemOb,
        ItemType.Harem: TbItemOb,
        ItemType.Head: TbItemOb,
        ItemType.TwistedEgg: TbItemOb,
        ItemType.CateMaterial: TbItemOb,
        ItemType.CateFood : TbItemOb,
        ItemType.QueenJewelryCheat: TbItemOb,
        ItemType.HeadFrame : TbItemOb,
        ItemType.Exchange : TbItemOb,
        ItemType.RehearseCheat: TbItemOb,
        ItemType.ChoosePackage : TbItemOb,
        ItemType.UnlockCostume : TbItemOb,
        ItemType.CostumeMaterial : TbItemOb,
        ItemType.Ambience: TbItemOb,
        ItemType.InterAction : TbItemOb,
        ItemType.BanquetCheat : TbItemOb,
        ItemType.Emoticons : TbItemOb,
        ItemType.Card : TbItemOb,
        ItemType.Background : TbItemOb,
        ItemType.CrossSeasonLottery: TbItemOb,
        ItemType.Gift: TbItemOb,
        ItemType.RedUnLimit: TbItemOb,
        ItemType.Bubble: TbItemOb,
        ItemType.ClearCheat: TbItemOb,

        # === 存储在TbResources
        ItemType.Silver: TbResourcesOb,
        ItemType.Food: TbResourcesOb,
        ItemType.Soldier: TbResourcesOb,
        ItemType.GameGold: TbResourcesOb,
        ItemType.Perception: TbResourcesOb,

        # === 存储在TbActivityItem
        ItemType.ActivityExpired: TbActivityItemOb,
        ItemType.SiegelordActivity: TbActivityItemOb,

        # == 特殊处理, 存储在TbActivityItem或TbItem
        ItemType.Character: TbBothItemOb,
    }

    @classmethod
    def factory(cls, item_type, item_id):
        """
        道具工厂
        """

        i = cls.key_to.get(int(item_type), None)
        if not i:
            # 为保证，道具的扣除操作是明确的，避免扣除道具时，无对应逻辑，
            # 开发人员需要明确自己添加的道具类型在key_to映射里，否则会抛出异常
            raise GameException('item {}:{} can not reduce'.format(item_type, item_id))
        return i

    @classmethod
    def reduce_item(cls, rid, item_type, item_id, num, log_param, negative=False, activity_id=None):
        """
        减少单个类型道具
        :param rid: 角色rid
        :param log_param: 日志数据
        :param item_type:
        :param item_id:
        :param num: 扣除数量
        :param negative: 是否可扣到负数
        :param activity_id: 活动id
        :return [bool 是否有减少, int 减少后剩余数量]
        """
        item_class = cls.factory(item_type, item_id)
        item_ob = item_class(rid, item_type, item_id, log_param=log_param, negative=negative, activity_id=activity_id)
        item_ob.reduce_and_log(num)

        return item_ob.get_num()

    @classmethod
    def reduce_many_item(cls, rid, item_list, log_param, negative=False, activity_id=None):
        """
        扣除多个道具
        """
        item_ob_list = []
        for item in item_list:
            item_type, item_id, count = item
            item_ob = cls.factory(item_type, item_id)(rid, item_type, item_id, log_param=log_param, negative=negative,
                                                      activity_id=activity_id, count=count)
            # 先校验剩余数量
            item_ob.check(item_ob.need_reduce_count)
            item_ob_list.append(item_ob)

        # 校验通过后，扣除
        return_data = []
        for item_ob in item_ob_list:
            item_ob.reduce_and_log(item_ob.need_reduce_count)
            return_data.append(item_ob.left_count())

        return return_data

